using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using Newtonsoft.Json;

namespace ConsoleApp1
{
    [StructLayout(LayoutKind.Sequential)]
    class CanFrame
    {
        public byte Info;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        public byte[] ID;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public byte[] Data;
    }

    public class JsonByteArrayConverter : JsonConverter
    {
        /// <summary>
        /// 写入Json数据
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="value"></param>
        /// <param name="serializer"></param>
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var result = new StringBuilder();
            if (value is byte[] temp)
                foreach (var item in temp)
                    result.AppendFormat("{0},", item);
            writer.WriteValue($"[{result.ToString().Trim(',')}]");
        }

        /// <summary>
        /// 读Json数据
        /// </summary>
        /// <param name="reader"></param>
        /// <param name="objectType"></param>
        /// <param name="existingValue"></param>
        /// <param name="serializer"></param>
        /// <returns></returns>
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
            JsonSerializer serializer)
        {
            var strValue = reader.Value.ToString();
            var temp = strValue.Split(new char[] { ',', '[', ']' }, StringSplitOptions.RemoveEmptyEntries);
            return temp.Select(x => byte.Parse(x)).ToArray();
        }

        /// <summary>
        /// 是否可以转换
        /// </summary>
        /// <param name="objectType"></param>
        /// <returns></returns>
        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(byte[]);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("CanFrame size:{0}", Marshal.SizeOf<CanFrame>());

            var frame = new CanFrame
            {
                Info = 0x88,
                ID = new byte[] { 0x12, 0x34, 0x56, 0x78 },
                Data = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }
            };
            var jsonStr = JsonConvert.SerializeObject(frame, new JsonByteArrayConverter());

            Console.WriteLine("CanFrame json:\n{0}", jsonStr);

            var bytes = StructToBytes(frame);
            Console.WriteLine("CanFrame bytes:\n{0}", byteArrayToHexString(bytes));

            //
            var frame2 = JsonConvert.DeserializeObject<CanFrame>(jsonStr, new JsonByteArrayConverter());

            Console.WriteLine("CanFrame2 bytes:\n{0}", byteArrayToHexString(StructToBytes(frame2)));

            // 
            var frame3 = BytesToStruct<CanFrame>(bytes);
            Console.WriteLine("CanFrame json2:\n{0}", JsonConvert.SerializeObject(frame3, new JsonByteArrayConverter()));

            Console.ReadLine();
        }

        public static Byte[] StructToBytes(Object structure)
        {
            int size = Marshal.SizeOf(structure);
            IntPtr buffer = Marshal.AllocHGlobal(size);

            try
            {
                Marshal.StructureToPtr(structure, buffer, false);

                var bytes = new Byte[size];
                Marshal.Copy(buffer, bytes, 0, size);
                return bytes;
            }
            catch
            {
                return null;
            }
            finally
            {
                Marshal.FreeHGlobal(buffer);
            }
        }

        public static T BytesToStruct<T>(Byte[] bytes)
        {
            int size = Marshal.SizeOf<T>();
            IntPtr buffer = Marshal.AllocHGlobal(size);

            try
            {
                Marshal.Copy(bytes, 0, buffer, size);

                return Marshal.PtrToStructure<T>(buffer);
            }
            catch
            {
                return default;
            }
            finally
            {
                Marshal.FreeHGlobal(buffer);
            }
        }

        public static string byteArrayToHexString(byte[] data)
        {
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < data.Length; i++)
            {
                builder.Append(string.Format("{0:X2} ", data[i]));
            }
            return builder.ToString().Trim();
        }

        public static byte[] hexStringToByteArray(string data)
        {
            string[] chars = data.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            byte[] returnBytes = new byte[chars.Length];
            //逐个字符变为16进制字节数据
            for (int i = 0; i < chars.Length; i++)
            {
                returnBytes[i] = Convert.ToByte(chars[i], 16);
            }
            return returnBytes;
        }
    }
}
